package com.ruoyi.biz.controller;

import lombok.Data;
import org.apache.poi.xwpf.usermodel.*;

import java.io.*;
import java.util.*;

public class TextSimilarityExtractor {
    // 滑动窗口大小（按字符计算）
    private static final int WINDOW_SIZE = 30;
    // 最小重复片段长度
    private static final int MIN_MATCH_LENGTH = 15;

    public static SimilarityResult compareDocuments(File file1, File file2) throws IOException {
        String text1 = readDocxContent(file1);
        String text2 = readDocxContent(file2);

        // 预处理文本
        String cleanText1 = cleanText(text1);
        String cleanText2 = cleanText(text2);

        // 生成文本指纹
        Map<String, List<Integer>> fingerprints1 = generateFingerprints(cleanText1);
        Map<String, List<Integer>> fingerprints2 = generateFingerprints(cleanText2);

        // 查找匹配片段
        List<MatchFragment> matches = findMatches(cleanText1, cleanText2, fingerprints1, fingerprints2);

        // 计算相似度
        double similarity = calculateSimilarity(cleanText1, cleanText2, matches);

        return new SimilarityResult(similarity, matches);
    }

    // 文档读取方法（同之前实现）
    private static String readDocxContent(File file) throws IOException {
        StringBuilder content = new StringBuilder();
        try (FileInputStream fis = new FileInputStream(file);
             XWPFDocument document = new XWPFDocument(fis)) {

            // 读取段落文本
            for (XWPFParagraph paragraph : document.getParagraphs()) {
                content.append(paragraph.getText()).append(" ");
            }

            // 读取表格文本
            for (XWPFTable table : document.getTables()) {
                for (XWPFTableRow row : table.getRows()) {
                    for (XWPFTableCell cell : row.getTableCells()) {
                        content.append(cell.getText()).append(" ");
                    }
                }
            }
        }
        return content.toString();
    }

    // 文本清洗方法（支持中文）
    private static String cleanText(String text) {
        // 匹配所有Unicode标点符号和符号字符
        String cleaned = text.toLowerCase()
                .replaceAll("[\\p{P}\\p{S}]", "") // 保留中文、字母、数字
                .replaceAll("\\s+", "");          // 合并空白
        return cleaned;
    }

    // 生成滑动窗口指纹
    private static Map<String, List<Integer>> generateFingerprints(String text) {
        Map<String, List<Integer>> fingerprints = new HashMap<>();
        for (int i = 0; i <= text.length() - WINDOW_SIZE; i++) {
            String window = text.substring(i, i + WINDOW_SIZE);
            fingerprints.computeIfAbsent(window, k -> new ArrayList<>()).add(i);
        }
        return fingerprints;
    }

    // 查找匹配片段
    private static List<MatchFragment> findMatches(String text1, String text2,
                                                   Map<String, List<Integer>> fp1,
                                                   Map<String, List<Integer>> fp2) {
        List<MatchFragment> matches = new ArrayList<>();

        // 遍历所有公共指纹
        fp1.keySet().retainAll(fp2.keySet());
        for (String window : fp1.keySet()) {
            for (int pos1 : fp1.get(window)) {
                for (int pos2 : fp2.get(window)) {
                    // 扩展匹配范围
                    int start1 = pos1;
                    int start2 = pos2;
                    int length = WINDOW_SIZE;

                    // 向前扩展
                    while (start1 > 0 && start2 > 0 &&
                            text1.charAt(start1-1) == text2.charAt(start2-1)) {
                        start1--;
                        start2--;
                        length++;
                    }

                    // 向后扩展
                    while (start1 + length < text1.length() &&
                            start2 + length < text2.length() &&
                            text1.charAt(start1 + length) == text2.charAt(start2 + length)) {
                        length++;
                    }

                    if (length >= MIN_MATCH_LENGTH) {
                        String content = text1.substring(start1, start1 + length);
                        matches.add(new MatchFragment(
                                content,
                                start1,
                                start1 + length,
                                start2,
                                start2 + length
                        ));
                    }
                }
            }
        }

        // 去重处理
        return mergeOverlaps(matches);
    }

    // 合并逻辑优化（防止索引越界）
    private static List<MatchFragment> mergeOverlaps(List<MatchFragment> fragments) {
        fragments.sort(Comparator.comparingInt(MatchFragment::getStart1));
        List<MatchFragment> merged = new ArrayList<>();

        if (fragments.isEmpty()) return merged;

        MatchFragment current = fragments.get(0);
        for (int i = 1; i < fragments.size(); i++) {
            MatchFragment frag = fragments.get(i);

            // 判断是否可合并（允许部分重叠或相邻）
            if (frag.getStart1() <= current.getEnd1() + 1) { // +1允许相邻
                int overlap = current.getEnd1() - frag.getStart1();
                String newContent;
                if (overlap > 0) {
                    newContent = current.getContent() +
                            frag.getContent().substring(Math.min(overlap, frag.getContent().length()));
                } else {
                    newContent = current.getContent() + frag.getContent();
                }

                current = new MatchFragment(
                        newContent,
                        Math.min(current.getStart1(), frag.getStart1()),
                        Math.max(current.getEnd1(), frag.getEnd1()),
                        Math.min(current.getStart2(), frag.getStart2()),
                        Math.max(current.getEnd2(), frag.getEnd2())
                );
            } else {
                merged.add(current);
                current = frag;
            }
        }
        merged.add(current);
        return merged;
    }

    // 相似度计算方法（防止超过100%）
    private static double calculateSimilarity(String text1, String text2,
                                              List<MatchFragment> matches) {
        int totalMatch = matches.stream()
                .mapToInt(f -> f.getContent().length())
                .sum();

        // 取较长文档的长度作为分母
        int maxLength = Math.max(text1.length(), text2.length());
        return maxLength == 0 ? 0 : (double) totalMatch / maxLength;
    }

    // 结果封装类
    @Data
    static class SimilarityResult {
        private final double similarity;
        private final List<MatchFragment> matches;

        public SimilarityResult(double similarity, List<MatchFragment> matches) {
            this.similarity = similarity;
            this.matches = matches;
        }

        // Getter方法
    }

    // 匹配片段记录
    @Data
    static class MatchFragment {
        private final String content;
        private final int start1;
        private final int end1;
        private final int start2;
        private final int end2;

        public MatchFragment(String content, int start1, int end1, int start2, int end2) {
            this.content = content;
            this.start1 = start1;
            this.end1 = end1;
            this.start2 = start2;
            this.end2 = end2;
        }

        // Getter方法
    }

    public static void main(String[] args) throws IOException {
        File doc1 = new File("D:/ruoyi/uploadPath/upload/2025/02/26/开题报告_20250226103115A001.docx");
        File doc2 = new File("D:/ruoyi/uploadPath/upload/2025/02/26/开题报告_20250226103115A001.docx");
        SimilarityResult result = compareDocuments(doc1, doc2);

        System.out.printf("整体相似度：%.2f%%\n", result.getSimilarity() * 100);
        System.out.println("重复片段：");
        result.getMatches().forEach(m ->
                System.out.printf("文档1[%d-%d] 与 文档2[%d-%d]：%s\n",
                        m.getStart1(), m.getEnd1(),
                        m.getStart2(), m.getEnd2(),
                        m.getContent())
        );
    }
}